package com.yijietong.yiliao.doctor.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * <pre>
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2016/08/02
 *     desc  : 加密解密相关的工具类
 * </pre>
 */
public final class EncryptUtils {

	private EncryptUtils() {
		throw new UnsupportedOperationException("u can't instantiate me...");
	}

	///////////////////////////////////////////////////////////////////////////
	// 哈希加密相关
	///////////////////////////////////////////////////////////////////////////

	/**
	 * MD2加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptMD2ToString(final String data) {
		return encryptMD2ToString(data.getBytes());
	}

	/**
	 * MD2加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptMD2ToString(final byte[] data) {
		return bytes2HexString(encryptMD2(data));
	}

	/**
	 * MD2加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptMD2(final byte[] data) {
		return hashTemplate(data, "MD2");
	}

	/**
	 * MD5加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptMD5ToString(final String data) {
		return encryptMD5ToString(data.getBytes());
	}

	/**
	 * MD5加密
	 *
	 * @param data
	 *            明文字符串
	 * @param salt
	 *            盐
	 * @return 16进制加盐密文
	 */
	public static String encryptMD5ToString(final String data, final String salt) {
		return bytes2HexString(encryptMD5((data + salt).getBytes()));
	}

	/**
	 * MD5加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptMD5ToString(final byte[] data) {
		return bytes2HexString(encryptMD5(data));
	}

	/**
	 * MD5加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param salt
	 *            盐字节数组
	 * @return 16进制加盐密文
	 */
	public static String encryptMD5ToString(final byte[] data, final byte[] salt) {
		if (data == null || salt == null)
			return null;
		byte[] dataSalt = new byte[data.length + salt.length];
		System.arraycopy(data, 0, dataSalt, 0, data.length);
		System.arraycopy(salt, 0, dataSalt, data.length, salt.length);
		return bytes2HexString(encryptMD5(dataSalt));
	}

	/**
	 * MD5加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptMD5(final byte[] data) {
		return hashTemplate(data, "MD5");
	}

	/**
	 * MD5加密文件
	 *
	 * @param filePath
	 *            文件路径
	 * @return 文件的16进制密文
	 */
	public static String encryptMD5File2String(final String filePath) {
		File file = isSpace(filePath) ? null : new File(filePath);
		return encryptMD5File2String(file);
	}

	/**
	 * MD5加密文件
	 *
	 * @param filePath
	 *            文件路径
	 * @return 文件的MD5校验码
	 */
	public static byte[] encryptMD5File(final String filePath) {
		File file = isSpace(filePath) ? null : new File(filePath);
		return encryptMD5File(file);
	}

	/**
	 * MD5加密文件
	 *
	 * @param file
	 *            文件
	 * @return 文件的16进制密文
	 */
	public static String encryptMD5File2String(final File file) {
		return bytes2HexString(encryptMD5File(file));
	}

	/**
	 * MD5加密文件
	 *
	 * @param file
	 *            文件
	 * @return 文件的MD5校验码
	 */
	public static byte[] encryptMD5File(final File file) {
		if (file == null)
			return null;
		FileInputStream fis = null;
		DigestInputStream digestInputStream;
		try {
			fis = new FileInputStream(file);
			MessageDigest md = MessageDigest.getInstance("MD5");
			digestInputStream = new DigestInputStream(fis, md);
			byte[] buffer = new byte[256 * 1024];
			while (true) {
				if (!(digestInputStream.read(buffer) > 0))
					break;
			}
			md = digestInputStream.getMessageDigest();
			return md.digest();
		} catch (NoSuchAlgorithmException | IOException e) {
			e.printStackTrace();
			return null;
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * SHA1加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptSHA1ToString(final String data) {
		return encryptSHA1ToString(data.getBytes());
	}

	/**
	 * SHA1加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptSHA1ToString(final byte[] data) {
		return bytes2HexString(encryptSHA1(data));
	}

	/**
	 * SHA1加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptSHA1(final byte[] data) {
		return hashTemplate(data, "SHA1");
	}

	/**
	 * SHA224加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptSHA224ToString(final String data) {
		return encryptSHA224ToString(data.getBytes());
	}

	/**
	 * SHA224加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptSHA224ToString(final byte[] data) {
		return bytes2HexString(encryptSHA224(data));
	}

	/**
	 * SHA224加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptSHA224(final byte[] data) {
		return hashTemplate(data, "SHA224");
	}

	/**
	 * SHA256加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptSHA256ToString(final String data) {
		return encryptSHA256ToString(data.getBytes());
	}

	/**
	 * SHA256加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptSHA256ToString(final byte[] data) {
		return bytes2HexString(encryptSHA256(data));
	}

	/**
	 * SHA256加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptSHA256(final byte[] data) {
		return hashTemplate(data, "SHA256");
	}

	/**
	 * SHA384加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptSHA384ToString(final String data) {
		return encryptSHA384ToString(data.getBytes());
	}

	/**
	 * SHA384加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptSHA384ToString(final byte[] data) {
		return bytes2HexString(encryptSHA384(data));
	}

	/**
	 * SHA384加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptSHA384(final byte[] data) {
		return hashTemplate(data, "SHA384");
	}

	/**
	 * SHA512加密
	 *
	 * @param data
	 *            明文字符串
	 * @return 16进制密文
	 */
	public static String encryptSHA512ToString(final String data) {
		return encryptSHA512ToString(data.getBytes());
	}

	/**
	 * SHA512加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 16进制密文
	 */
	public static String encryptSHA512ToString(final byte[] data) {
		return bytes2HexString(encryptSHA512(data));
	}

	/**
	 * SHA512加密
	 *
	 * @param data
	 *            明文字节数组
	 * @return 密文字节数组
	 */
	public static byte[] encryptSHA512(final byte[] data) {
		return hashTemplate(data, "SHA512");
	}

	/**
	 * hash加密模板
	 *
	 * @param data
	 *            数据
	 * @param algorithm
	 *            加密算法
	 * @return 密文字节数组
	 */
	private static byte[] hashTemplate(final byte[] data, final String algorithm) {
		if (data == null || data.length <= 0)
			return null;
		try {
			MessageDigest md = MessageDigest.getInstance(algorithm);
			md.update(data);
			return md.digest();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * HmacMD5加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacMD5ToString(final String data, final String key) {
		return encryptHmacMD5ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacMD5加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacMD5ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacMD5(data, key));
	}

	/**
	 * HmacMD5加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacMD5(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacMD5");
	}

	/**
	 * HmacSHA1加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA1ToString(final String data, final String key) {
		return encryptHmacSHA1ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacSHA1加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA1ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacSHA1(data, key));
	}

	/**
	 * HmacSHA1加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacSHA1(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacSHA1");
	}

	/**
	 * HmacSHA224加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA224ToString(final String data, final String key) {
		return encryptHmacSHA224ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacSHA224加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA224ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacSHA224(data, key));
	}

	/**
	 * HmacSHA224加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacSHA224(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacSHA224");
	}

	/**
	 * HmacSHA256加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA256ToString(final String data, final String key) {
		return encryptHmacSHA256ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacSHA256加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA256ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacSHA256(data, key));
	}

	/**
	 * HmacSHA256加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacSHA256(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacSHA256");
	}

	/**
	 * HmacSHA384加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA384ToString(final String data, final String key) {
		return encryptHmacSHA384ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacSHA384加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA384ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacSHA384(data, key));
	}

	/**
	 * HmacSHA384加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacSHA384(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacSHA384");
	}

	/**
	 * HmacSHA512加密
	 *
	 * @param data
	 *            明文字符串
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA512ToString(final String data, final String key) {
		return encryptHmacSHA512ToString(data.getBytes(), key.getBytes());
	}

	/**
	 * HmacSHA512加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 16进制密文
	 */
	public static String encryptHmacSHA512ToString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptHmacSHA512(data, key));
	}

	/**
	 * HmacSHA512加密
	 *
	 * @param data
	 *            明文字节数组
	 * @param key
	 *            秘钥
	 * @return 密文字节数组
	 */
	public static byte[] encryptHmacSHA512(final byte[] data, final byte[] key) {
		return hmacTemplate(data, key, "HmacSHA512");
	}

	/**
	 * Hmac加密模板
	 *
	 * @param data
	 *            数据
	 * @param key
	 *            秘钥
	 * @param algorithm
	 *            加密算法
	 * @return 密文字节数组
	 */
	private static byte[] hmacTemplate(final byte[] data, final byte[] key, final String algorithm) {
		if (data == null || data.length == 0 || key == null || key.length == 0)
			return null;
		try {
			SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
			Mac mac = Mac.getInstance(algorithm);
			mac.init(secretKey);
			return mac.doFinal(data);
		} catch (InvalidKeyException | NoSuchAlgorithmException e) {
			e.printStackTrace();
			return null;
		}
	}

	///////////////////////////////////////////////////////////////////////////
	// DES加密相关
	///////////////////////////////////////////////////////////////////////////

	/**
	 * DES转变
	 * <p>
	 * 法算法名称/加密模式/填充方式
	 * </p>
	 * <p>
	 * 加密模式有：电子密码本模式ECB、加密块链模式CBC、加密反馈模式CFB、输出反馈模式OFB
	 * </p>
	 * <p>
	 * 填充方式有：NoPadding、ZerosPadding、PKCS5Padding
	 * </p>
	 */
	public static String DES_Transformation = "DES/ECB/NoPadding";
	private static final String DES_Algorithm = "DES";

	/**
	 * DES加密后转为Base64编码
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            8字节秘钥
	 * @return Base64密文
	 */
	public static byte[] encryptDES2Base64(final byte[] data, final byte[] key) {
		return base64Encode(encryptDES(data, key));
	}

	/**
	 * DES加密后转为16进制
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            8字节秘钥
	 * @return 16进制密文
	 */
	public static String encryptDES2HexString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptDES(data, key));
	}

	/**
	 * DES加密
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            8字节秘钥
	 * @return 密文
	 */
	public static byte[] encryptDES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, DES_Algorithm, DES_Transformation, true);
	}

	/**
	 * DES解密Base64编码密文
	 *
	 * @param data
	 *            Base64编码密文
	 * @param key
	 *            8字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptBase64DES(final byte[] data, final byte[] key) {
		return decryptDES(base64Decode(data), key);
	}

	/**
	 * DES解密16进制密文
	 *
	 * @param data
	 *            16进制密文
	 * @param key
	 *            8字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptHexStringDES(final String data, final byte[] key) {
		return decryptDES(hexString2Bytes(data), key);
	}

	/**
	 * DES解密
	 *
	 * @param data
	 *            密文
	 * @param key
	 *            8字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptDES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, DES_Algorithm, DES_Transformation, false);
	}

	///////////////////////////////////////////////////////////////////////////
	// 3DES加密相关
	///////////////////////////////////////////////////////////////////////////

	/**
	 * 3DES转变
	 * <p>
	 * 法算法名称/加密模式/填充方式
	 * </p>
	 * <p>
	 * 加密模式有：电子密码本模式ECB、加密块链模式CBC、加密反馈模式CFB、输出反馈模式OFB
	 * </p>
	 * <p>
	 * 填充方式有：NoPadding、ZerosPadding、PKCS5Padding
	 * </p>
	 */
	public static String TripleDES_Transformation = "DESede/ECB/NoPadding";
	private static final String TripleDES_Algorithm = "DESede";

	/**
	 * 3DES加密后转为Base64编码
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            24字节秘钥
	 * @return Base64密文
	 */
	public static byte[] encrypt3DES2Base64(final byte[] data, final byte[] key) {
		return base64Encode(encrypt3DES(data, key));
	}

	/**
	 * 3DES加密后转为16进制
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            24字节秘钥
	 * @return 16进制密文
	 */
	public static String encrypt3DES2HexString(final byte[] data, final byte[] key) {
		return bytes2HexString(encrypt3DES(data, key));
	}

	/**
	 * 3DES加密
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            24字节密钥
	 * @return 密文
	 */
	public static byte[] encrypt3DES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, TripleDES_Algorithm, TripleDES_Transformation, true);
	}

	/**
	 * 3DES解密Base64编码密文
	 *
	 * @param data
	 *            Base64编码密文
	 * @param key
	 *            24字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptBase64_3DES(final byte[] data, final byte[] key) {
		return decrypt3DES(base64Decode(data), key);
	}

	/**
	 * 3DES解密16进制密文
	 *
	 * @param data
	 *            16进制密文
	 * @param key
	 *            24字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptHexString3DES(final String data, final byte[] key) {
		return decrypt3DES(hexString2Bytes(data), key);
	}

	/**
	 * 3DES解密
	 *
	 * @param data
	 *            密文
	 * @param key
	 *            24字节密钥
	 * @return 明文
	 */
	public static byte[] decrypt3DES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, TripleDES_Algorithm, TripleDES_Transformation, false);
	}

	///////////////////////////////////////////////////////////////////////////
	// AES加密相关
	///////////////////////////////////////////////////////////////////////////

	/**
	 * AES转变
	 * <p>
	 * 法算法名称/加密模式/填充方式
	 * </p>
	 * <p>
	 * 加密模式有：电子密码本模式ECB、加密块链模式CBC、加密反馈模式CFB、输出反馈模式OFB
	 * </p>
	 * <p>
	 * 填充方式有：NoPadding、ZerosPadding、PKCS5Padding
	 * </p>
	 */
	public static String AES_Transformation = "AES/ECB/NoPadding";
	private static final String AES_Algorithm = "AES";

	/**
	 * AES加密后转为Base64编码
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return Base64密文
	 */
	public static byte[] encryptAES2Base64(final byte[] data, final byte[] key) {
		return base64Encode(encryptAES(data, key));
	}

	/**
	 * AES加密后转为16进制
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return 16进制密文
	 */
	public static String encryptAES2HexString(final byte[] data, final byte[] key) {
		return bytes2HexString(encryptAES(data, key));
	}

	/**
	 * AES加密
	 *
	 * @param data
	 *            明文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return 密文
	 */
	public static byte[] encryptAES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, AES_Algorithm, AES_Transformation, true);
	}

	/**
	 * AES解密Base64编码密文
	 *
	 * @param data
	 *            Base64编码密文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptBase64AES(final byte[] data, final byte[] key) {
		return decryptAES(base64Decode(data), key);
	}

	/**
	 * AES解密16进制密文
	 *
	 * @param data
	 *            16进制密文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptHexStringAES(final String data, final byte[] key) {
		return decryptAES(hexString2Bytes(data), key);
	}

	/**
	 * AES解密
	 *
	 * @param data
	 *            密文
	 * @param key
	 *            16、24、32字节秘钥
	 * @return 明文
	 */
	public static byte[] decryptAES(final byte[] data, final byte[] key) {
		return desTemplate(data, key, AES_Algorithm, AES_Transformation, false);
	}

	/**
	 * DES加密模板
	 *
	 * @param data
	 *            数据
	 * @param key
	 *            秘钥
	 * @param algorithm
	 *            加密算法
	 * @param transformation
	 *            转变
	 * @param isEncrypt
	 *            {@code true}: 加密 {@code false}: 解密
	 * @return 密文或者明文，适用于DES，3DES，AES
	 */
	public static byte[] desTemplate(final byte[] data, final byte[] key, final String algorithm,
			final String transformation, final boolean isEncrypt) {
		if (data == null || data.length == 0 || key == null || key.length == 0)
			return null;
		try {
			SecretKeySpec keySpec = new SecretKeySpec(key, algorithm);
			Cipher cipher = Cipher.getInstance(transformation);
			SecureRandom random = new SecureRandom();
			cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, keySpec, random);
			return cipher.doFinal(data);
		} catch (Throwable e) {
			e.printStackTrace();
			return null;
		}
	}

	private static final char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
			'F' };

	private static String bytes2HexString(final byte[] bytes) {
		if (bytes == null)
			return null;
		int len = bytes.length;
		if (len <= 0)
			return null;
		char[] ret = new char[len << 1];
		for (int i = 0, j = 0; i < len; i++) {
			ret[j++] = hexDigits[bytes[i] >>> 4 & 0x0f];
			ret[j++] = hexDigits[bytes[i] & 0x0f];
		}
		return new String(ret);
	}

	private static byte[] hexString2Bytes(String hexString) {
		if (isSpace(hexString))
			return null;
		int len = hexString.length();
		if (len % 2 != 0) {
			hexString = "0" + hexString;
			len = len + 1;
		}
		char[] hexBytes = hexString.toUpperCase().toCharArray();
		byte[] ret = new byte[len >> 1];
		for (int i = 0; i < len; i += 2) {
			ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1]));
		}
		return ret;
	}

	private static int hex2Dec(final char hexChar) {
		if (hexChar >= '0' && hexChar <= '9') {
			return hexChar - '0';
		} else if (hexChar >= 'A' && hexChar <= 'F') {
			return hexChar - 'A' + 10;
		} else {
			throw new IllegalArgumentException();
		}
	}

	private static byte[] base64Encode(final byte[] input) {
		Base64.Encoder encoder = Base64.getEncoder();
		return encoder.encode(input);
	}

	private static byte[] base64Decode(final byte[] input) {
		Base64.Decoder decoder = Base64.getDecoder();
		return decoder.decode(input);
	}

	private static boolean isSpace(final String s) {
		if (s == null)
			return true;
		for (int i = 0, len = s.length(); i < len; ++i) {
			if (!Character.isWhitespace(s.charAt(i))) {
				return false;
			}
		}
		return true;
	}
}
